package main

import (
	"fmt"
	"os"
	"risc-v/ld/pkg/linker"
	"risc-v/ld/pkg/utils"
	"strings"
)

// make docker_build
// make docker_run
func main() {
	ctx := linker.NewContext()

	remaining := parseArgs(ctx)
	//fmt.Println(remaining)

	// 当前文件未解析到 机器类型
	if ctx.Args.Emulation == linker.MachineTypeNone {
		for _, s := range remaining {
			if strings.HasPrefix(s, "-") {
				continue
			}
			file := linker.MustNewFile(s)
			ctx.Args.Emulation = linker.GetMachineTypeFromContent(file.Content)
			if ctx.Args.Emulation != linker.MachineTypeNone {
				break
			}
		}
	}

	if ctx.Args.Emulation != linker.MachineTypeRISCV64 {
		utils.Fatal("unknown machine type")
	}

	linker.ReadInputFile(ctx, remaining) // 读取文件(*.o,*.a)
	linker.ResolveSymbols(ctx)           // 解析符号
	linker.RegisterSectionPieces(ctx)    // 处理 section
	linker.ComputeMergedSectionSize(ctx) // 计算合并后的节的大小和offset

	linker.CreateSyntheticSections(ctx)
	linker.BindSection(ctx)
	ctx.Chunks = append(ctx.Chunks, linker.CollectOutPutSections(ctx)...) // 加入 OutPutSection 到 context 的 Chunks 中
	linker.ScanRelocations(ctx)                                           // 解决重定位
	linker.ComputeSectionSizes(ctx)                                       // 计算节大小
	linker.SortOutPutSections(ctx)                                        // 按照ELF 文件格式进行排序
	for _, chunk := range ctx.Chunks {
		chunk.UpdateSectionHeader(ctx)
	}

	fileSize := linker.SetOutPutSectionOffset(ctx) //设置实际将要写入的文件的偏移
	//fmt.Println(fileSize)

	ctx.Buf = make([]byte, fileSize)

	file, err := os.OpenFile(ctx.Args.OutPut, os.O_RDWR|os.O_CREATE, 0777)
	utils.MustNo(err)

	for _, chunk := range ctx.Chunks {
		chunk.CopyBuf(ctx)
	}

	_, err = file.Write(ctx.Buf) // 写文件
	utils.MustNo(err)
}

// dashes 为 name 添加"-" 和"--",如果是单个字母只添加 "-"
func dashes(name string) []string {
	if len(name) == 1 {
		return []string{"-" + name}
	}
	return []string{"-" + name, "--" + name}
}

func parseArgs(ctx *linker.Context) []string {
	// 去掉当前文件名
	args := os.Args[1:]
	fmt.Println(args)
	arg := ""

	readArg := func(name string) bool {
		for _, opt := range dashes(name) {
			// 如果找到了对应的参数
			if args[0] == opt {
				if len(args) == 1 {
					utils.Fatal(fmt.Sprintf("option -%s:argument missing", name))
				}

				// 提取到参数
				arg = args[1]
				// args 前移两位 option和对应的 value
				// 同时上面已经判断 args 长度 args 不会越界
				args = args[2:]
				return true
			}
			// 未匹配 -option [或 --option] value 形式的参数
			// 尝试匹配 -option=value 或 -melfriscv64
			prefix := opt
			if len(name) > 1 {
				prefix += "="
			}

			if strings.HasPrefix(args[0], prefix) {
				arg = args[0][len(prefix):] //切割出参数
				args = args[1:]             // args 前移一位即可
				return true
			}
		}
		return false
	}

	readFlag := func(name string) bool {
		for _, opt := range dashes(name) {
			if args[0] == opt {
				args = args[1:]
				return true
			}
		}
		return false
	}

	// 保留的未解析的参数
	remaining := make([]string, 0)

	for len(args) > 0 {
		if readFlag("help") {
			// help 信息
			fmt.Println(linker.Help)
			os.Exit(1)
		} else if readFlag("v") || readFlag("version") {
			// 版本信息
			fmt.Println(linker.Version)
			os.Exit(1)
		}

		if readArg("o") || readArg("output") {
			// 指定输出参数
			ctx.Args.OutPut = arg
		} else if readArg("m") {
			if arg == "elf64lriscv" {
				ctx.Args.Emulation = linker.MachineTypeRISCV64
			} else {
				utils.Fatal(fmt.Sprintf("unknown m argumant -%s", arg))
			}
		} else if readArg("L") {
			ctx.Args.LibraryPaths = append(ctx.Args.LibraryPaths, arg)
		} else if readArg("l") {
			remaining = append(remaining, "-l"+arg)
		} else if readArg("sysroot") ||
			readFlag("static") ||
			readArg("plugin") ||
			readArg("plugin-opt") ||
			readFlag("as-needed") ||
			readFlag("start-group") ||
			readFlag("end-group") ||
			readArg("hash-style") ||
			readArg("build-id") ||
			readFlag("s") ||
			readFlag("no-relax") ||
			readArg("z") {
			// ignored
		} else {
			if args[0][0] == '-' {
				utils.Fatal(fmt.Sprintf("unknow command line option: %s", args[0]))
			}
			remaining = append(remaining, args[0])
			args = args[1:]
		}
	}

	return remaining
}
